package com.imaker.dao;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Restrictions;

import com.imaker.domain.Page;
import com.imaker.utils.ListHelper;
import com.imaker.utils.PageHelper;
import com.imaker.utils.Validate;

public class BaseDao<T> {
	private final Log log = LogFactory.getLog(BaseDao.class);

	private SessionFactory sessionFactory;

	private Class<T> entityClass;

	/**
	 * 构造函数
	 * 
	 * @param sessionFactory
	 * @param entityClass
	 */
	public BaseDao(SessionFactory sessionFactory, Class<T> entityClass) {
		this.sessionFactory = sessionFactory;
		this.entityClass = entityClass;
	}

	/**
	 * 构造函数
	 */
	public BaseDao(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}
	
	/**
	 * 保存对象
	 * 
	 * @param entity
	 *            需要保存的对象
	 * @throws CRUDException
	 */
	public void save(T entity) throws Exception {
		try {
			getSession().save(entity);
		} catch (Exception e) {
			System.out.println(e.getMessage());
			log.error(e);
			throw new Exception(e);
		}
	}

	/**
	 * 修改对象
	 * 
	 * @param entity
	 *            需要修改的对象
	 * @throws Exception
	 */
	public void update(T entity) throws Exception {
		try {
			getSession().update(entity);
		} catch (HibernateException e) {
			System.out.println(e.getMessage());
			log.error(e);
			throw new Exception(e);
		}
	}

	/**
	 * 保存或修改<br/> 若被操作对象在数据库中不存在则保存,反之则修改
	 * 
	 * @param entity
	 *            需要操作的对象
	 * @throws Exception
	 */
	public void saveOrUpdate(T entity) throws Exception {
		try {
			getSession().saveOrUpdate(entity);
		} catch (HibernateException e) {
			System.out.println(e.getMessage());
			log.error(e);
			throw new Exception(e);
		}
	}

	/**
	 * 删除对象
	 * 
	 * @param entity
	 *            需要删除的对象
	 * @throws Exception
	 */
	public void delete(T entity) throws Exception {
		try {
			getSession().delete(entity);
		} catch (HibernateException e) {
			System.out.println(e.getMessage());
			log.error(e);
			throw new Exception(e);
		}
	}

	/**
	 * 按ID删除对象
	 * 
	 * @param id
	 *            需要删除的对象ID
	 * @throws Exception
	 */
	public void delete(Serializable id) throws Exception {
		T entity = get(id);
		if(entity!=null)
			delete(entity);
	}

	/**
	 * 批量删除
	 * 
	 * @param collection
	 *            对象集合
	 * @throws Exception
	 */
	public void deleteAll(Collection<T> collection) throws Exception {
		try {
			for (Iterator iter = collection.iterator(); iter.hasNext();) {
				getSession().delete(iter.next());
			}
		} catch (HibernateException e) {
			System.out.println(e.getMessage());
			log.error(e);
			throw new Exception(e);
		}
	}

	/**
	 * 批量删除
	 * 
	 * @param idList
	 *            对象ID集合
	 * @throws Exception
	 */
	public void deleteByIdList(List<String> idList) throws Exception {
		try {
			for (Iterator iter = idList.iterator(); iter.hasNext();) {
				delete((Serializable) iter.next());
			}
		} catch (HibernateException e) {
			System.out.println(e.getMessage());
			log.error(e);
			throw new Exception(e);
		}
	}

	/**
	 * 找出指定ID集合中不能被删除的ID集合
	 * 
	 * @param idList
	 * @return
	 * @throws Exception
	 */
	public List<List<String>> findCannotDeleteList(List<String> idList,
			List<String> sqlList) throws Exception {
		// 声明Set集合
		Set<String> cannotDeleteData = new HashSet<String>();
		// 循环SQL常量类中删除用到的SQL集合
		for (String hql : sqlList) {
			// 以ID为参数循环查询数据
			for (String id : idList) {
				// 如果查找到数据,则将这个ID放入不能被删除的集合中
				if (Validate.collectionNotNull(find(hql, id)))
					cannotDeleteData.add(id);
			}
		}
		// 返回不能被删除的List集合
		return ListHelper.getDifferent(idList, new ArrayList<String>(
				cannotDeleteData));
	}

	/**
	 * 查询全部
	 * 
	 * @return
	 * @throws Exception
	 */
	public List<T> findAll() throws Exception {
		return findByCriteria();
	}

	/**
	 * 按ID获取对象(LOAD)<br/> 如果未查询到数据,会抛出ObjectNotFoundEcception
	 * 
	 * @param id
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public T load(final Serializable id) throws Exception {
		try {
			return (T) getSession().load(entityClass, id);
		} catch (HibernateException e) {
			System.out.println(e.getMessage());
			log.error(e);
			throw new Exception(e);
		}
	}

	/**
	 * 按ID获取对象(GET)<br/> 如果未查询到数据,则返回NULL
	 * 
	 * @param id
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public T get(final Serializable id) throws Exception {
		try {
			return (T) getSession().get(entityClass, id);
		} catch (HibernateException e) {
			System.out.println(e.getMessage());
			log.error(e);
			throw new Exception(e);
		}
	}

	/**
	 * 按HQL查询对象列表.
	 * 
	 * @param hql
	 *            hql语句
	 * @param values
	 *            数量可变的参数
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public List<T> find(String hql, Object... values) throws Exception {
		try {
			return createQuery(hql, values).list();
		} catch (HibernateException e) {
			System.out.println(e.getMessage());
			log.error(e);
			throw new Exception(e);
		}
	}

	/**
	 * 按HQL查询唯一对象.
	 * 
	 * @param hql
	 * @param values
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public Object findUnique(String hql, Object... values) throws Exception {
		try {
			return createQuery(hql, values).uniqueResult();
		} catch (HibernateException e) {
			System.out.println(e.getMessage());
			log.error(e);
			throw new Exception(e);
		}
	}

	/**
	 * 按Criterion查询对象列表.
	 * 
	 * @param criterion
	 *            数量可变的Criterion
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	private List<T> findByCriteria(Criterion... criterion) throws Exception {
		try {
			return createCriteria(criterion).list();
		} catch (HibernateException e) {
			System.out.println(e.getMessage());
			log.error(e);
			throw new Exception(e);
		}
	}

	/**
	 * 按属性查找对象列表<br/> 示例代码<br/> xxx.findByProperty("name", "Lucy");
	 * 
	 * @param propertyName
	 *            属性名
	 * @param value
	 *            属性值
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public List<T> findByProperty(String propertyName, Object value)
			throws Exception {
		try {
			return createCriteria(Restrictions.eq(propertyName, value)).list();
		} catch (HibernateException e) {
			System.out.println(e.getMessage());
			log.error(e);
			throw new Exception(e);
		}
	}

	/**
	 * 按属性查找唯一对象.若对象不唯一会抛出异常哦<br/> 示例代码<br/>
	 * xxx.findUniqueByProperty("name","Lucy");
	 * 
	 * @param propertyName
	 *            属性名
	 * @param value
	 *            属性值
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public T findUniqueByProperty(String propertyName, Object value)
			throws Exception {
		try {
			return (T) createCriteria(Restrictions.eq(propertyName, value))
					.uniqueResult();
		} catch (HibernateException e) {
			System.out.println(e.getMessage());
			log.error(e);
			throw new Exception(e);
		}
	}

	/**
	 * 根据查询函数与参数列表创建Query对象,后续可进行更多处理,辅助函数.
	 * 
	 * @param hql
	 * @param values
	 * @return
	 */
	private Query createQuery(String hql, Object... values) {
		Query queryObject = getSession().createQuery(hql);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				queryObject.setParameter(i, values[i]);
			}
		}
		queryObject.setCacheable(true);
		return queryObject;
	}

	/**
	 * 根据Criterion条件创建Criteria,后续可进行更多处理,辅助函数.
	 * 
	 * @param criterions
	 * @return
	 */
	private Criteria createCriteria(Criterion... criterions) {
		Criteria criteria = getSession().createCriteria(entityClass);
		for (Criterion c : criterions) {
			criteria.add(c);
		}
		criteria.setCacheable(true);
		return criteria;
	}

	/**
	 * 根据原生SQL查询
	 * 
	 * @param sql
	 * @return 返回集合,每个对象为Object[]
	 * @throws Exception
	 */
	public List findBySQL(String sql) throws Exception {
		try {
			return getSQLQuery(sql).list();
		} catch (HibernateException e) {
			System.out.println(e.getMessage());
			log.error(e);
			throw new Exception(e);
		}
	}

	/**
	 * 根据原生SQL查询
	 * 
	 * @param sql
	 * @return 返回集合,每个对象为entityClass
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public List<T> findBySQLAsEntity(String sql) throws Exception {
		try {
			return getSQLQuery(sql).addEntity(entityClass).list();
		} catch (HibernateException e) {
			System.out.println(e.getMessage());
			log.error(e);
			throw new Exception(e);
		}
	}

	/**
	 * 以SQL方式执行语句
	 * 
	 * @param sql
	 * @param values
	 * @throws Exception
	 */
	public void executeUpdate(String sql, Object... values)
			throws Exception {
		try {
			SQLQuery query = getSQLQuery(sql);
			if (Validate.arrayNotNull(values)) {
				for (int i = 0; i < values.length; i++) {
					query.setParameter(i, values[i]);
				}
			}
			query.executeUpdate();
		} catch (HibernateException e) {
			System.out.println(e.getMessage());
			log.error(e);
			throw new Exception(e);
		}
	}

	/**
	 * 分页方法
	 * 
	 * @param Page对象
	 * @param 查询语句
	 * @param 排序条件
	 * @return HQL参数
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public Page<T> pagination(Page<T> page, String hql, String orderBy, Object... values) throws Exception {
		if (Validate.notNull(orderBy)) {
			orderBy = " order by " + orderBy;
		} else {
			orderBy = "";
		}

		Query query = createQuery(hql + orderBy, values);
		
		// 设置分页参数
		query.setFirstResult(PageHelper.getFirstRow(page.getCurrPage(), page.getPageSize()));
		query.setMaxResults(page.getPageSize());

		// count效率高
		Query countQuery = createQuery("SELECT COUNT(id) " + hql, values);
		
		//设置查询缓存
		query.setCacheable(true);
		countQuery.setCacheable(true);
		
		// 查找总行数
		int rowCount = ((Long) countQuery.uniqueResult()).intValue();
		
		// 结果放入分页类
		page.setRowCount(rowCount);
		page.setList(query.list());
		page.setMaxPage(PageHelper.getMaxPage(rowCount, page.getPageSize()));
		// 返回分页类
		return page;
	}
	
	/**
	 * 分页方法
	 * 
	 * @param page
	 * @param hql
	 * @param values
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public Page<T> paginationBySQL(Page<T> page, String hql, String orderBy, Object... values) throws Exception {
		if (Validate.notNull(orderBy)) {
			orderBy = " order by " + orderBy;
		} else {
			orderBy = "";
		}

		SQLQuery query = getSession().createSQLQuery("select * " + hql + orderBy);
		if(Validate.arrayNotNull(values)) {
			for (int i = 0; i < values.length; i++) {
				query.setParameter(i, values[i]);
			}
		}

		// 设置分页参数
		query.setFirstResult(PageHelper.getFirstRow(page.getCurrPage(), page.getPageSize()));
		query.setMaxResults(page.getPageSize());

		// count效率高
		SQLQuery countQuery = getSession().createSQLQuery("SELECT COUNT(id) " + hql);
		if(Validate.arrayNotNull(values)) {
			for (int i = 0; i < values.length; i++) {
				query.setParameter(i, values[i]);
			}
		}
		// 查找总行数
		int rowCount = (Integer)countQuery.uniqueResult();
		// 结果放入分页类
		page.setRowCount(rowCount);
		page.setList(query.list());
		page.setMaxPage(PageHelper.getMaxPage(rowCount, page.getPageSize()));
		// 返回分页类
		return page;
	}

	/**
	 * 获得SQLQuery
	 * 
	 * @param sql
	 * @return
	 */
	private SQLQuery getSQLQuery(String sql) {
		SQLQuery query = getSession().createSQLQuery(sql);
		return query;
	}

	/**
	 * 获取原生Session
	 * 
	 * @return
	 */
	public Session getSession() {
		return sessionFactory.getCurrentSession();
	}
}
